package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.heima.article.dto.ArticleDto;
import com.heima.article.dto.ArticleHomeDto;
import com.heima.article.dto.ArticleInfoDto;
import com.heima.article.dto.ArticleStreamMessage;
import com.heima.article.entity.ApArticle;
import com.heima.article.entity.ApArticleContent;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.IApArticleContentService;
import com.heima.article.service.IApArticleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.task.ArticleHtmlTasks;
import com.heima.common.dto.ResponseResult;
import com.heima.common.enums.AppHttpCodeEnum;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * <p>
 * 文章信息表，存储已发布的文章 服务实现类
 * </p>
 *
 * @author Mr_Su
 * @since 2022-07-01
 */
@Service
@Slf4j
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements IApArticleService {

    @Autowired
    private IApArticleContentService apArticleContentService;

    @Autowired
    private ArticleHtmlTasks articleHtmlTasks;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public ResponseResult<Long> saveApArticle(ArticleDto dto) {
        // 判断是否存在文章id
        if (dto.getId() == null) {
            // 新增文章信息
            ApArticle apArticle = new ApArticle();
            BeanUtils.copyProperties(dto, apArticle);
            apArticle.setCreatedTime(new Date());
            this.save(apArticle);

            // 保存文章内容信息
            String content = dto.getContent();
            ApArticleContent apArticleContent = new ApArticleContent();
            apArticleContent.setArticleId(apArticle.getId());
            apArticleContent.setContent(content);
            apArticleContentService.save(apArticleContent);

            // 调用生成静态页面的方法
            articleHtmlTasks.createArticleHtml(apArticle, dto.getContent());

            return ResponseResult.okResult(apArticle.getId());
        } else {
            // 修改文章信息
            ApArticle apArticle = new ApArticle();
            BeanUtils.copyProperties(dto, apArticle);
            apArticle.setCreatedTime(new Date());
            this.updateById(apArticle);

            // 修改文章内容信息
            LambdaUpdateWrapper<ApArticleContent> lqwApArticleContent = new LambdaUpdateWrapper<>();
            lqwApArticleContent.set(ApArticleContent::getContent, dto.getContent());
            lqwApArticleContent.eq(ApArticleContent::getArticleId, dto.getId());
            apArticleContentService.update(lqwApArticleContent);

            // 调用生成静态页面的方法
            articleHtmlTasks.createArticleHtml(apArticle, dto.getContent());

            return ResponseResult.okResult(apArticle.getId());
        }
    }

    @Override
    public ResponseResult load(ArticleHomeDto dto, int type) {

        // 判断是否是是首页
        if (dto.getLoaddir() == 1) {
            List<ApArticle> articleList = new ArrayList<>();
            String key = "hot_article_first_page_" + dto.getChannelId();
            long start = 0;
            long end = dto.getSize() - 1;
            Set<String> strings = redisTemplate.boundZSetOps(key).reverseRange(start, end);
            for (String string : strings) {
                ApArticle apArticle = JSON.parseObject(string, ApArticle.class);
                articleList.add(apArticle);
            }
            // 判断查询到的数量是否 >= dto.getSize() 意味着redis中数量不够的话,从mysql中查询
            if (articleList.size() >= dto.getSize()) {
                return ResponseResult.okResult(articleList);
            }
        }

        LambdaQueryWrapper<ApArticle> lqwApArticle = new LambdaQueryWrapper<>();
        if (type == 0) {
            // 0代表初次加载或者加载更新 发布时间>maxTime
            lqwApArticle.gt(ApArticle::getPublishTime, dto.getMaxTime());
        } else {
            // 1代表加载更多 发布时间<minTime
            lqwApArticle.lt(ApArticle::getPublishTime, dto.getMinTime());
        }

        // 查询条件:频道id 如果频道id是0,就是查询所有频道
        //        如果dto中的channelId不为0时 根据频道查询
        if (dto.getChannelId() != 0) {
            lqwApArticle.eq(ApArticle::getChannelId, dto.getChannelId());
        }

        // 隐藏了两个条件: 是否下架 是否删除
        lqwApArticle.eq(ApArticle::getIsDown, false);
        lqwApArticle.eq(ApArticle::getIsDelete, false);
        // 根据发布时间排序
        lqwApArticle.orderByDesc(ApArticle::getPublishTime);
        // 分页
        Page<ApArticle> page = new Page<>(1, dto.getSize());
        this.page(page, lqwApArticle);
        return ResponseResult.okResult(page.getRecords());
    }

    @Override
    public ResponseResult loadArticleInfo(ArticleInfoDto dto) {
        // 根据文章id查询ap_article
        ApArticle apArticle = this.getById(dto.getArticleId());
        if (apArticle == null) {
            log.info("查询不到数据,文章id是: " + dto.getArticleId());
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        // 根据文章id查询ap_article_context
        LambdaQueryWrapper<ApArticleContent> lqwApArticleContent = new LambdaQueryWrapper<>();
        lqwApArticleContent.eq(ApArticleContent::getArticleId, dto.getArticleId());
        ApArticleContent apArticleContent = apArticleContentService.getOne(lqwApArticleContent);

        Map resultMap = new HashMap();
        resultMap.put("config", apArticle);
        resultMap.put("content", apArticleContent);
        return ResponseResult.okResult(resultMap);
    }

    @Override // 定时计算热点文章,并放入redis中
    public void computeHotArticle() {
        // 查询前300天未下架未删除的文章

        // 获取300天之前的日期
        Date beforeDate = DateTime.now().minusDays(300).toDate();
        // 构建查询条件
        LambdaQueryWrapper<ApArticle> lqwApArticle = new LambdaQueryWrapper<>();
        lqwApArticle.eq(ApArticle::getIsDelete, false);
        lqwApArticle.eq(ApArticle::getIsDown, false);
        lqwApArticle.gt(ApArticle::getPublishTime, beforeDate);

        List<ApArticle> articleList = this.list(lqwApArticle);

        // 计算每个文章的分值
        for (ApArticle apArticle : articleList) {
            Integer score = computeScore(apArticle);
            // 放入Redis中 结构:使用Zset redis中的key：hot_article_first_page_频道ID

            ApArticle cache = new ApArticle();

            cache.setId(apArticle.getId());
            cache.setTitle(apArticle.getTitle());
            cache.setImages(apArticle.getImages());
            cache.setAuthorId(apArticle.getAuthorId());
            cache.setAuthorName(apArticle.getAuthorName());
            cache.setLayout(apArticle.getLayout());
            cache.setStaticUrl(apArticle.getStaticUrl());
            cache.setPublishTime(apArticle.getPublishTime());
            redisTemplate.boundZSetOps("hot_article_first_page_" + apArticle.getChannelId()).add(JSON.toJSONString(cache), score);
            redisTemplate.boundZSetOps("hot_article_first_page_0").add(JSON.toJSONString(apArticle), score);
        }
    }

    private Integer computeScore(ApArticle apArticle) {
        // 阅读数 * 1 + 点赞数 * 3 + 评论数 * 5 + 收藏数 * 8
        Integer score = 0;

        if (apArticle.getViews() != null) {
            score += apArticle.getViews();
        }
        if (apArticle.getLikes() != null) {
            score += apArticle.getLikes() * 3;
        }
        if (apArticle.getComment() != null) {
            score += apArticle.getComment() * 5;
        }
        if (apArticle.getCollection() != null) {
            score += apArticle.getCollection() * 8;
        }
        return score;
    }

    @Override
    public void updateHotArticle(ArticleStreamMessage articleStreamMessage) {
        Long articleId = articleStreamMessage.getArticleId();
        ApArticle apArticle = this.getById(articleId);

        ApArticle cache = new ApArticle();

        cache.setId(apArticle.getId());
        cache.setTitle(apArticle.getTitle());
        cache.setImages(apArticle.getImages());
        cache.setAuthorId(apArticle.getAuthorId());
        cache.setAuthorName(apArticle.getAuthorName());
        cache.setLayout(apArticle.getLayout());
        cache.setStaticUrl(apArticle.getStaticUrl());
        cache.setPublishTime(apArticle.getPublishTime());

        // 修改redis中缓存的那个文章的分值 根据存储的对象获取分值
        Double score = redisTemplate.boundZSetOps("hot_article_first_page_" + apArticle.getChannelId()).score(JSON.toJSONString(cache));
        Integer todayScore = getTodayScore(articleStreamMessage);
        if (score != null) { // 代表Redis中有这个数据,把分值修改后再放回redis中
            // articleId:"1456088799662469121",view:1 , like:1, comment:1 , collect:2


            redisTemplate.boundZSetOps("hot_article_first_page_" + apArticle.getChannelId()).add(JSON.toJSONString(cache), score + todayScore);
            redisTemplate.boundZSetOps("hot_article_first_page_0").add(JSON.toJSONString(cache), score + todayScore);
        } else { // 代表Redis中没有这个数据,直接放值
            redisTemplate.boundZSetOps("hot_article_first_page_" + apArticle.getChannelId()).add(JSON.toJSONString(cache), todayScore);
            redisTemplate.boundZSetOps("hot_article_first_page_0").add(JSON.toJSONString(cache), todayScore);
        }
        // 修改mysql中文章表中的那个文章的各个操作数
        LambdaUpdateWrapper<ApArticle> luwApArticle = new LambdaUpdateWrapper<>();
        luwApArticle.eq(ApArticle::getId, articleId);
        luwApArticle.setSql("views=views+" + articleStreamMessage.getView());
        luwApArticle.setSql("likes=likes+" + articleStreamMessage.getLike());
        luwApArticle.setSql("comment=comment+" + articleStreamMessage.getComment());
        luwApArticle.setSql("collection=collection+" + articleStreamMessage.getCollect());
    }

    private Integer getTodayScore(ArticleStreamMessage articleStreamMessage) {
        int score = 0;
        score += articleStreamMessage.getView() * 1 * 3;
        score += articleStreamMessage.getLike() * 3 * 3;
        score += articleStreamMessage.getComment() * 5 * 3;
        score += articleStreamMessage.getCollect() * 8 * 3;
        return score;
    }
}
